Os dados de focos foram obtidos do banco de dados do INPE: https://queimadas.dgi.inpe.br/queimadas/bdqueimadas#exportar-dados
Os dados foram filtrados pelos limites Amazônia legal, entre os anos de 2011 e 2020.
A distância à rodovia mais próxima foi calculada em função de um shapefile de rodovias disponibilizado pelo IBGE. O mapa de grau de urbanização também foi obtido do IBGE. Os dados de áreas desmatadas são elaborados pelo INPE através do PRODES.
Os pacotes sf e terra são essenciais para a manipulação de dados vetoriais e matriciais respectivamente. O pacote tidyverse contém funções uteis para a manipulação de tabelas. O pacote spatstat é utilizado para extrair estatisticas espaciais como a densidade de focos. O pacote caret é responsável pelo treinamento e aplicação dos modelos.
library(terra)
library(tidyverse)
library(sf)
library(spatstat)
library(caret)
library(doParallel)
library(gbm)
A ideia geral aqui é criar uma imagem raster com a contagem da ocorrência de focos por km². Primeiro os dados de foco devem ser carregados. Eu aproveito para ler o shapefile da amazônia legal também.
focos <- st_read("focos_2011_2020.shp")
## Reading layer `focos_2011_2020' from data source
## `F:\Pablo\Documentos\Doutorado\side\focos_2011_2020.shp' using driver `ESRI Shapefile'
## Simple feature collection with 1215884 features and 15 fields
## Geometry type: POINT
## Dimension: XY
## Bounding box: xmin: 2828897 ymin: 8004499 xmax: 6111664 ymax: 10573720
## Projected CRS: SIRGAS 2000 / Brazil Polyconic
am_legal <- st_read("amazonia_legal.shp")
## Reading layer `amazonia_legal' from data source
## `F:\Pablo\Documentos\Doutorado\side\amazonia_legal.shp' using driver `ESRI Shapefile'
## Simple feature collection with 1 feature and 1 field
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 2794537 ymin: 8004219 xmax: 6112012 ymax: 10586380
## Projected CRS: SIRGAS 2000 / Brazil Polyconic
am_vect <- vect(am_legal)
Em seguida criar um raster vazio de resolução de 10km para preencher os dados:
focos_km2 <- rast(ext=ext(focos), crs=crs(focos), resolution=10000)
E enfim contar os focos por pixel da imagem:
focos_km2 <- rasterize(vect(focos), focos_km2, fun=length, field=1, background=0) %>%
mask(am_vect)
colorPal <- colorRampPalette(rev(RColorBrewer::brewer.pal(11, "RdYlGn")))(256)
plot(main="Focos brutos por 10km²", focos_km2, col=colorPal)
Ou então fazer uma análise de densidade:
pts <- st_coordinates(focos) %>%
unique()
pts_ppp <- ppp(pts[,1], pts[,2], window=as.owin(am_legal), check=FALSE)
A densidade é diferente da quantidade bruta de focos por pixel no sentido de que ela é suavizada em função de uma banda (distância). A função padrão de suavização é a gaussiana.
Um teste rapido usando diferentes bandas:
bandas <- c(10000, 25000, 50000, 100000)
dens <- lapply(bandas, function(x) rast(density(pts_ppp, sigma=x))*1e6)
dens <- rast(dens)
names(dens) <- c("10km", "25km", "50km", "100km")
plot(dens, col=colorPal)
Nesse caso uma banda de 50km pareceu apropriada.
pt_dens <- density(pts_ppp, sigma=50000, eps=10000)
pt_dens <- rast(pt_dens)*1e6
crs(pt_dens) <- crs(focos_km2)
pt_dens <- resample(pt_dens, focos_km2)
plot(main="Densidade de focos por km²", pt_dens, col=colorPal)
Lado a lado:
r_stack <- rast(list(focos_km2, pt_dens))
names(r_stack) <- c("Focos por 10km²", "Densidade de focos")
plot(r_stack, col=colorPal)
É importante lembrar que os valores calculados representam um histórico de 10 anos, portanto o valor anual seria a média, ou o valor apresentado dividido por 10.
Finalmente, salvar os resultados:
writeRaster(focos_km2, "focos_10km.tif", gdal=c("COMPRESS=DEFLATE", "PREDICTOR=2"), overwrite=T)
writeRaster(pt_dens, "densidade.tif", gdal=c("COMPRESS=DEFLATE", "PREDICTOR=2"), overwrite=T)
Os mapas dessas variáveis ja foi feito usando o QGIS, então é só carregar eles.
variaveis <- list(
grau_urb = rast("grau_urb.tif"),
dist_rod = rast("dist_rod.tif"),
dist_desm = rast("dist_desm.tif"),
dist_ind = rast("dist_ind.tif"),
dist_cons = rast("dist_cons.tif")
)
variaveis <- lapply(variaveis, function(x){
resample(x, variaveis[[1]]) %>%
mask(am_vect)
})
variaveis <- rast(variaveis)
plot(variaveis, col=viridis::viridis(256))
A princípio a amostragem vai ser feita por via de pontos aleatórios. Por enquanto vamos usar 5000 pontos:
set.seed(1)
amostras <- st_sample(am_legal, 5000) %>%
st_sf('ID' = seq(length(.)), 'geometry' = .)
ggplot()+
geom_sf(data=am_legal, col="gray", fill=NA)+
geom_sf(data=amostras, col="red", pch= 3, alpha=0.3)+
theme_bw()
Extração dos dados rasteriais e conversão para tabela:
amostras <- vect(amostras)
dados_y <- tibble(
focos_10km2=terra::extract(focos_km2, amostras)[,2],
densidade=terra::extract(pt_dens, amostras)[,2]
)
dados_x <- terra::extract(variaveis, amostras) %>%
as_tibble() %>%
select(-ID)
dados <- bind_cols(dados_y,dados_x)
Traduzindo em forma de gráfico:
dados %>%
select(-focos_10km2) %>%
mutate(across(-c(densidade, grau_urb), ~ .x/1000)) %>%
pivot_longer(-densidade) %>%
ggplot(aes(value, densidade))+
geom_point(alpha=0.2)+
xlab("Valor")+
ylab("Densidade")+
facet_wrap(vars(name), ncol = 3, scales = "free_x")
fitControl <- trainControl(## 10-fold CV
method = "repeatedcv",
number = 10,
## repeated ten times
repeats = 10)
rfGrid <- expand.grid(mtry = c(2,3,4,5))
gbmGrid <- expand.grid(interaction.depth = c(1, 5, 9),
n.trees = (1:30)*50,
shrinkage = 0.1,
n.minobsinnode = 20)
cl <- makePSOCKcluster(5)
registerDoParallel(cl)
modelo_rf <- dados %>%
drop_na(densidade) %>%
train(densidade ~ grau_urb + dist_rod + dist_desm + dist_ind + dist_cons, data=.,
trControl = fitControl,
tuneGrid = rfGrid,)
stopCluster(cl)
write_rds(modelo_rf, "modelo_rf.rds")
Estatisticas do modelo:
modelo_rf
## Random Forest
##
## 4955 samples
## 5 predictor
##
## No pre-processing
## Resampling: Cross-Validated (10 fold, repeated 10 times)
## Summary of sample sizes: 4459, 4459, 4459, 4461, 4459, 4459, ...
## Resampling results across tuning parameters:
##
## mtry RMSE Rsquared MAE
## 2 0.1270520 0.6769336 0.08797339
## 3 0.1255838 0.6830307 0.08581174
## 4 0.1248307 0.6863201 0.08456322
## 5 0.1243623 0.6884374 0.08367233
##
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was mtry = 5.
plot(modelo_rf)
plot(varImp(modelo_rf))
Comparação com a realidade:
dados2 <- dados %>%
mutate(preds = predict(modelo_rf, newdata=.), erro=(densidade-preds))
dados2 %>%
select(-focos_10km2) %>%
pivot_longer(-c(densidade, preds, erro)) %>%
pivot_longer(-c(name, erro, value), names_to = "tipo", values_to = "densidade") %>%
ggplot(aes(value, densidade, color=tipo))+
geom_point(alpha=0.3)+
facet_wrap(vars(name), ncol=2, scales="free_x")+
scale_color_discrete("Legenda: ",labels=c("Previsto", "Observado"))+
theme(legend.position = "bottom")
Distribuição de erros:
dados2 %>%
ggplot(aes(x=erro))+
geom_histogram(bins=50)+
scale_x_continuous(breaks=seq(-1,1,0.1))
Enfim classificar a imagem:
pred_rast <- predict(variaveis, modelo_rf, na.rm=T, cores=2)
writeRaster(pred_rast, "rf_rast.tif", gdal=c("COMPRESS=DEFLATE", "PREDICTOR=2"), overwrite=TRUE)
plot(pred_rast, col=colorPal)
par(mfrow=c(1,2))
plot(main="Previsto", pred_rast, col=colorPal)
plot(main="Observado", pt_dens, col=colorPal)
cl <- makePSOCKcluster(5)
registerDoParallel(cl)
modelo_gbm <- dados %>%
drop_na(densidade) %>%
train(densidade ~ grau_urb + dist_rod + dist_desm + dist_ind + dist_cons,
data=.,
method = "gbm",
trControl = fitControl,
tuneGrid = gbmGrid,
verbose = FALSE)
stopCluster(cl)
write_rds(modelo_gbm, "modelo_gbm.rds")
Estatisticas do modelo:
plot(modelo_gbm)
plot(varImp(modelo_gbm))
Comparação com a realidade:
dados3 <- dados %>%
mutate(preds = predict(modelo_gbm, newdata=.), erro=(densidade-preds))
dados3 %>%
select(-focos_10km2) %>%
pivot_longer(-c(densidade, preds, erro)) %>%
pivot_longer(-c(name, erro, value), names_to = "tipo", values_to = "densidade") %>%
ggplot(aes(value, densidade, color=tipo))+
geom_point(alpha=0.3)+
facet_wrap(vars(name), ncol=2, scales="free_x")+
scale_color_discrete("Legenda: ",labels=c("Previsto", "Observado"))+
theme(legend.position = "bottom")
Distribuição de erros:
dados3 %>%
ggplot(aes(x=erro))+
geom_histogram(bins=50)+
scale_x_continuous(breaks=seq(-1,1,0.1))
Aplicação do modelo:
pred_rast <- predict(variaveis, modelo_gbm, na.rm=T, cores=2)
writeRaster(pred_rast, "gbm_rast.tif", gdal=c("COMPRESS=DEFLATE", "PREDICTOR=2"), overwrite=TRUE)
plot(pred_rast, col=colorPal)
par(mfrow=c(1,2))
plot(main="Previsto", pred_rast, col=colorPal)
plot(main="Observado", pt_dens, col=colorPal)
cl <- makePSOCKcluster(5)
registerDoParallel(cl)
modelo_xgb <- dados %>%
drop_na(densidade) %>%
train(densidade ~ grau_urb + dist_rod + dist_desm + dist_ind + dist_cons,
data=.,
method = 'xgbTree',
trControl = fitControl,
verbose = FALSE)
stopCluster(cl)
write_rds(modelo_xgb, "modelo_xgb.rds")
plot(modelo_xgb)
dados4 <- dados %>%
mutate(preds = predict(modelo_xgb, newdata=.), erro=(densidade-preds))
dados4 %>%
select(-focos_10km2) %>%
pivot_longer(-c(densidade, preds, erro)) %>%
pivot_longer(-c(name, erro, value), names_to = "tipo", values_to = "densidade") %>%
ggplot(aes(value, densidade, color=tipo))+
geom_point(alpha=0.3)+
facet_wrap(vars(name), ncol=2, scales="free_x")+
scale_color_discrete("Legenda: ",labels=c("Previsto", "Observado"))+
theme(legend.position = "bottom")
Distribuição de erros:
dados4 %>%
ggplot(aes(x=erro))+
geom_histogram(bins=50)+
scale_x_continuous(breaks=seq(-1,1,0.1))
Aplicação do modelo:
pred_rast <- predict(variaveis, modelo_xgb, na.rm=T, cores=2)
writeRaster(pred_rast, "xgb_rast.tif", gdal=c("COMPRESS=DEFLATE", "PREDICTOR=2"), overwrite=TRUE)
plot(pred_rast, col=colorPal)
ggplot()+
geom_freqpoly(aes(dados2$erro, color="RF"), bins=100)+
geom_freqpoly(aes(dados3$erro, color="GBM"), bins=100)+
geom_freqpoly(aes(dados4$erro, color="XGB"), bins=100)+
theme_bw()+
xlab("Erro")+
ylab("Frequência")+
theme(legend.title = element_blank())
rmse_modelos <- tibble(
"RF" = min(modelo_rf$results$RMSE),
"GBM" = min(modelo_gbm$results$RMSE),
"XGB" = min(modelo_xgb$results$RMSE)
) %>%
pivot_longer(everything())
rmse_modelos %>%
arrange(value) %>%
ggplot(aes(reorder(name, -value), value))+
geom_col(fill="black", width=0.3)+
ylab("RMSE")+
xlab("Modelo")